package com.wxz.gblw.cjg.utils.generator.config;

import freemarker.template.Configuration;
import freemarker.template.Template;

import java.io.*;
import java.sql.*;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 模板工具类
 * @author xiangzhong.wang
 * @date 2019-12-31 14:06
 */
public class FreemarkerUtils {
    private Pattern linePattern = Pattern.compile("_(\\w)");
    private Connection connection;
    private GeneratorConfig config;

    private Map<String, String> typeMap = new HashMap<String, String>() {{
        put("CHAR", "String");
        put("VARCHAR", "String");
        put("TEXT", "String");
        put("LONGTEXT", "String");
        put("SMALLINT", "Short");
        put("TINYINT", "Byte");
        put("INT", "Integer");
        put("FLOAT", "Float");
        put("BIGINT", "Long");
        put("DOUBLE", "Double");
        put("BIT", "Boolean");
        put("TIME", "Date");
        put("TIMESTAMP", "Date");
        put("DATE", "Date");
        put("DATETIME", "Date");
        put("DECIMAL", "java.math.BigDecimal");
    }};

    public FreemarkerUtils(GeneratorConfig config) {
        this.config = config;

        getConnection();
    }

    /**
     * 下划线转驼峰
     *
     * @param str
     * @return
     */
    private String lineToHump(String str) {
        str = str.toLowerCase();
        Matcher matcher = linePattern.matcher(str);
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            matcher.appendReplacement(sb, matcher.group(1).toUpperCase());
        }
        matcher.appendTail(sb);
        return sb.toString();
    }

    /**
     * 首字母大写
     *
     * @param letter
     * @return
     */
    private String upperFirstLatter(String letter) {
        return letter.substring(0, 1).toUpperCase() + letter.substring(1);
    }

    /**
     * 连接数据库
     *
     * @return
     */
    private Connection getConnection() {
        connection = null;
        try {
            Class.forName(config.getDriver());
            connection = DriverManager.getConnection(config.getUrl(), config.getUser(), config.getPassword());
        } catch (Exception e) {
            e.printStackTrace();
        }

        return connection;
    }

    /**
     * 获取所有表
     *
     * @return
     */
    private ResultSet getTables() {
        try {
            DatabaseMetaData databaseMetaData = connection.getMetaData();
            return databaseMetaData.getTables(null, "%", null, new String[]{"TABLE"});
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     * 获取表的所有列
     *
     * @param tableName
     * @return
     */
    private ResultSet getColumns(String tableName) {
        ResultSet resultSet = null;
        try {
            DatabaseMetaData databaseMetaData = connection.getMetaData();
            resultSet = databaseMetaData.getColumns(null, "%", tableName, "%");
        } catch (Exception e) {
            e.printStackTrace();
        }

        return resultSet;
    }

    public void execute() {
        Map<String, Object> dataMap = new HashMap<>();
        ResultSet res = getTables();
        try {
            while (res.next()) {
                String table = res.getString("TABLE_NAME");
                //忽略的表
                if(config.getIgnoreTables().contains(table)) {
                    continue;
                }

                SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                String date = dateFormat.format(new Date());
                String model = table;
                //去掉前缀
                for(String prefix: config.getIgnorePrefix()) {
                    if(table.indexOf(prefix) == 0) {
                        model = model.substring(prefix.length(), model.length());
                        break;
                    }
                }
                model = upperFirstLatter(lineToHump(model));
                dataMap.put("table", table);
                dataMap.put("model", model);
                dataMap.put("description", model);
                dataMap.put("classPath", config.getClassPath());
                dataMap.put("author", config.getAuthor());
                dataMap.put("date", date);
                //generatorEntityBase(dataMap);
                generatorEntity(dataMap);
                generatorMapper(dataMap);
                generatorService(dataMap);
                generatorController(dataMap);
                //generatorTest(dataMap);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

//    private void generatorTest(Map<String, Object> dataMap) {
//        String path = config.getPath().replace("/main/", "/test/");
//        generator(path + "/controller", "test.ftl", "ControllerTest", dataMap, config.getCoverFiles().contains("test"));
//    }

    private void generatorController(Map<String, Object> dataMap) {
        generator(config.getPath() + "/controller", "controller.ftl", "Controller", dataMap, config.getCoverFiles().contains("controller"));
    }

    private void generatorService(Map<String, Object> dataMap) {
        generator(config.getPath() + "/service", "service.ftl", "Service", dataMap, config.getCoverFiles().contains("service"));
    }

    private void generatorMapper(Map<String, Object> dataMap) {
        generator(config.getPath() + "/mapper", "mapper.ftl", "Mapper", dataMap, config.getCoverFiles().contains("mapper"));
    }

//    private void generatorEntity(Map<String, Object> dataMap) {
//        generator(config.getPath() + "/entity", "entity.ftl", "", dataMap, config.getCoverFiles().contains("entity"));
//    }

    private void generatorEntity(Map<String, Object> dataMap) {
        ResultSet resultSet = getColumns(dataMap.get("table").toString());
        List<Field> fields = new ArrayList<>();

        if (resultSet != null) {
            try {
                while (resultSet.next()) {
                    String columnName = resultSet.getString("COLUMN_NAME");
                    String columnType = resultSet.getString("TYPE_NAME");
                    String remarks = resultSet.getString("REMARKS");

                    //忽略字段
                    if (columnName.equals("id")
                            || columnName.equals("create_user")
                            || columnName.equals("create_time")
                            || columnName.equals("update_user")
                            || columnName.equals("update_time")
                            || config.getIgnoreColumns().contains(columnName)) {
                        continue;
                    }

                    String name = columnName;
                    //去掉前缀
                    for(String prefix: config.getIgnoreColPrefix()) {
                        if(name.indexOf(prefix) == 0) {
                            name = name.substring(prefix.length(), name.length());
                            break;
                        }
                    }

                    name = lineToHump(name);

                    fields.add(new Field(name, columnName, typeMap.get(columnType), columnType, remarks));
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

        dataMap.put("fields", fields);

        generator(config.getPath() + "/entity", "entity.ftl", "", dataMap, config.getCoverFiles().contains("entity"));
    }

    private void generator(String path, String tpl, String type, Map<String, Object> dataMap, boolean isCover) {
        Configuration configuration = new Configuration(Configuration.VERSION_2_3_23);
        Writer out = null;
        try {

            configuration.setClassForTemplateLoading(this.getClass(), "/ftls");
            Template template = configuration.getTemplate(tpl);
            String fileName = dataMap.get("model") + type + ".java";
            File dir = new File(path);
            if (!dir.exists()) {
                dir.mkdirs();
            }


            File docFile = new File(path + "\\" + fileName);

            if (!isCover && docFile.exists()) {
                System.out.println("^^^^^^^^^^^^^^^^^^^^^^^^" + fileName + "文件已存在 !");
                return;
            }
            out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(docFile)));
            // step6 输出文件
            template.process(dataMap, out);
            System.out.println("^^^^^^^^^^^^^^^^^^^^^^^^" + fileName + "文件创建成功 !");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (null != out) {
                    out.flush();
                }
            } catch (Exception e2) {
                e2.printStackTrace();
            }
        }
    }
}